import json
import logging
import math
import multiprocessing
import os
import re
import struct
import sys
import threading
import time
import traceback
from multiprocessing import shared_memory
from logging.handlers import RotatingFileHandler

import numpy as np
from PySide6 import QtCore
from PySide6.QtCore import QSize, QTimer
from PySide6.QtGui import Qt, QGuiApplication, QCursor
from PySide6.QtWidgets import QMessageBox, QFileDialog, QInputDialog, QLineEdit

from communication.second_process import Second_Process
from communication.tcp_connect import Tcp_Connect
from config.config import Config
from config.operation_database import Operation_Database
from extend_ui.channel_manage import Channel_Manage
from extend_ui.communication_manage import Communication_Manage
from extend_ui.e2prom_manage import E2prom_Manage
from extend_ui.err_log import log_decorator
from extend_ui.program_update import Program_Update
from extend_ui.struct_write_manage import Struct_Write_Manage
from extend_ui.tcp_reset_manage import Tcp_Reset_Manage
from main_ui.Tab_Table import Tab_Table
from main_ui.center_widget import Center_Widget
from main_ui.dock_console import Dock_Console
from main_ui.dock_scope import Dock_Scope, Scope_Widget
from main_ui.dock_tree import Dock_Tree
from main_ui.file_process import File_Process
from main_ui.main_window_ui import Main_Window_Ui
import resources.resources_rc  # 导入图片资源不可去


class Main_Window(Main_Window_Ui):
    """ 主窗口类 负责关联所有 信号 和 槽 """
    signal_table_info = QtCore.Signal(int, list, list)  # 负责添加控制台控件和刷新数据
    signal_operat = QtCore.Signal(int, int, list)  # 负责控制窗口事件和动作响应
    signal_online_stateChanged = QtCore.Signal(bool)  # 响应上下线状态的信号

    def __init__(self):
        super(Main_Window, self).__init__()
        # self.setEnable_all_button(False)
        self.init_variable()
        self.init_err_log()
        self.setEnable_file_button(False)
        self.create_dataBase_connect()
        self.connect_siggnal_slot()
        self.read_config()
        self.init_class()
        self.init_timer()
        self.load_class()

    def init_variable(self):
        """ 初始化所需变量 """
        # 获取日志的实例化
        # logging.basicConfig(
        #     level=logging.DEBUG,
        #     filemode='a',
        #     filename='host_ui_err.log',
        #     encoding='utf-8',
        #     format='%(asctime)s \n %(name)s - %(levelname)s \n %(module)s - %(lineno)d \n %(message)s \n'
        # )
        # self.logger = logging.getLogger(__name__)

        # 创建消息队列
        self.queue_scope_data = multiprocessing.Queue(maxsize=0)  # 用于读取scope数据的队列
        self.queue_second_instruct = multiprocessing.Queue(maxsize=0)  # 用于给第二进程发送指令
        self.queue_second_state = multiprocessing.Queue(maxsize=0)  # 用于接收第二进程的响应
        self.queue_logging = multiprocessing.Queue(maxsize=0)  # 用于接收第二进程的响应

        # 创建共享内存
        """
        create   创建新的空间
        size     字节数        2000*100*100*4是指 显示个数*间隔数*通道个数*每个的字节数
        name     名字
        """
        try:
            self.share_mem_time = shared_memory.SharedMemory(create=True,
                                                             size=Scope_Widget.custom_mem_size * 4,
                                                             name='scope_share_memory_time')

            self.share_mem_data = shared_memory.SharedMemory(create=True,
                                                             size=Scope_Widget.custom_mem_size * Scope_Widget.max_channel_count * 4,
                                                             name='scope_share_memory_data')

            self.share_mem_index = shared_memory.SharedMemory(create=True,
                                                              size=4,
                                                              name='scope_share_memory_index')
        except Exception as e:
            QMessageBox.warning(self, '警告', '已经有了打开的程序或未完全关闭的进程，\n请处理后重试')
            self.close()
            sys.exit()

        # 声明变量
        self.center_widget = None
        self.conn_flag = False  # 通信连接的标志位
        self.change_database_flag = False  # 切换数据库的标志
        self.config_all = None  # 存储所有的config
        self.table_info = {}  # 表数据的缓存
        self.operation_database = None  # 对数据库操作的实例
        self.db_path = ''  # 数据库所在路径
        self.h5_path = ''  # 保存 h5 文件的路径
        self.config_var_table = None  # 存储var表的实例
        self.h5_compression = None  # h5文件的压缩格式
        self.dock_tree = None  # dock_tree的实例
        self.tab_table = None  # tab_table的实例
        self.console = None  # dock_console的实例
        self.dock_scope = None  # 波形视图的实例
        self.channel_manage = None  # 通道管理的实例
        self.communication_manage = None  # 通讯管理的实例
        self.e2prom_manage = None  # e2prom窗口的实例
        self.e2prom_change_flag = False  # 修改e2prom状态后用该标志位响应答复
        self.e2prom_IP_flag = False  # e2prom写入IP的标志
        self.e2prom_IP_str = None   # 临时存放写入e2prom中的IP地址
        self.e2prom_response_count = 0  # 等待响应的计数器
        self.program_update = None  # 远程升级的实例
        self.struct_write_manage = None # 写入结构体实例
        self.struct_write_recover_scope_flag = False    # 判断是否需要恢复scope状态
        self.QMSG_dialog_flag = True  # 判断是否需要弹窗
        self.scope_recv_flag = False  # 用于判断是否据需接收scope
        self.var_current_list = []  # 用来存储下线之前的当前值
        self.tcp_reset = None  # 存储tcp_reset的实例
        self.host_save_name = None  # 存储host文件名字
        self.host_save_flag = False  # 存储host文件标志
        self.host_save_replace_count = 10  # 存储host的更新次数
        self.header_path = None  # 存储导出头文件的路径
        self.header_prefix = ''  # 存储导出头文件的前缀
        self.header_name = ''  # 存储导出头文件的变量名
        self.logging_level = 50     # 定义日志等级的初始值
        self.scope_time_ext_flag = False    # scope保存是否启用时间后缀
        self.scope_time_ext_format = '年-月日-时分'   # 设置默认的时间戳格式
        self.grid_size = [50, 50]
        self.screen_ratio = [1920, 1080]

    def init_err_log(self):
        """
        日志等级(level)
        DEBUG	    详细信息，只有诊断问题时才需要，就是一般的调试信息
        INFO	    当程序运行时期望的一些信息
        WARNING	    软件运行正常，但是可能会有一些预期之外的事件发生
        ERROR	    由于一些严重问题导致软件一些功能出现问题
        CRITICAL	很严重的错误直接导致软件不能继续运行
        """
        """
        格式化参数(format)
        %（acstime)s 时间
        %（filename)s 日志文件名
        %（funcName)s 调用日志的函数名
        %（levelname)s 日志的级别
        %（module)s 调用日志的模块名
        %（message)s 日志信息
        %（name)s logger的name，不写的话默认是root
        """
        # 自定义等级字典
        self.logging_level_dict = {
            'DEBUG': 10,
            'INFO': 20,
            'WARNING': 30,
            'ERROR': 40,
            'CRITICAL': 50
        }

        log_filename = 'main.log'
        max_bytes = 1024 * 1024  # 最大文件大小（1MB）
        backup_count = 5  # 保留文件的个数

        # 定义全局变量my_logger，方便在其他地方调用输出日志
        self.my_logger = logging.getLogger('my_logger')
        self.my_logger.setLevel(self.logging_level)
        handler = RotatingFileHandler(log_filename, encoding='utf-8', maxBytes=max_bytes, backupCount=backup_count)
        format = logging.Formatter('%(asctime)s - %(levelname)s \n %(message)s \n')
        handler.setFormatter(format)
        self.my_logger.addHandler(handler)

        handler2 = logging.StreamHandler()  # 控制台输出处理器
        handler2.setLevel(logging.WARNING)
        self.my_logger.addHandler(handler2)

    def clear_errLog_slot(self):
        replay = QMessageBox.information(self, '删除', '是否清除所有日志文件!', QMessageBox.Yes|QMessageBox.Cancel)
        if replay == QMessageBox.Yes:
            for i in range(5):
                try:
                    # 关闭所有处理器
                    for handler in self.my_logger.handlers:
                        handler.close()
                        self.my_logger.removeHandler(handler)
                    # 删除所有日志文件
                    if os.path.exists('main.log'):
                        os.remove('main.log')
                    if os.path.exists('main.log.1'):
                        os.remove('main.log.1')
                    if os.path.exists('main.log.2'):
                        os.remove('main.log.2')
                    if os.path.exists('main.log.3'):
                        os.remove('main.log.3')
                    if os.path.exists('main.log.4'):
                        os.remove('main.log.4')
                    if os.path.exists('main.log.5'):
                        os.remove('main.log.5')
                    # 重新打开日志处理
                    self.init_err_log()
                    QMessageBox.information(self, '提示', '日志清除成功!', QMessageBox.Yes)
                    return

                except Exception as e:
                    trace_back = traceback.format_exc()
                    # QMessageBox.information(self, '提示', '日志清除失败！\n错误原因:' + trace_back, QMessageBox.Yes)
        else:
            return

        # trace_back = traceback.format_exc()
        QMessageBox.information(self, '提示', '日志清除失败！\n错误原因:' + trace_back, QMessageBox.Yes)
        try:
            self.init_err_log()
        except Exception as e:
            pass


    def clear_err_log(self):
        try:
            # 关闭所有处理器
            for handler in self.my_logger.handlers:
                handler.close()
                self.my_logger.removeHandler(handler)
            # 删除所有日志文件
            if os.path.exists('main.log'):
                os.remove('main.log')
            if os.path.exists('main.log.1'):
                os.remove('main.log.1')
            if os.path.exists('main.log.2'):
                os.remove('main.log.2')
            if os.path.exists('main.log.3'):
                os.remove('main.log.3')
            if os.path.exists('main.log.4'):
                os.remove('main.log.4')
            if os.path.exists('main.log.5'):
                os.remove('main.log.5')
            # 重新打开日志处理
            self.init_err_log()
            QMessageBox.information(self, '提示', '日志清除成功!', QMessageBox.Yes)
        except Exception as e:
            self.clear_err_log()
            # trace_back = traceback.format_exc()
            # QMessageBox.information(self, '提示', '日志清除失败！\n错误原因:' + trace_back, QMessageBox.Yes)



    def create_dataBase_connect(self):
        """ 获取数据库地址，并创建数据库连接实例 """
        if os.path.exists('.path'):
            with open('.path', 'r', encoding='utf-8') as f:
                self.db_path = json.load(f)[0]
        else:
            with open('.path', 'w', encoding='utf-8') as f:
                self.db_path = ''
                json.dump([self.db_path], f, ensure_ascii=False, indent=4, separators=(',', ':'))
        self.operation_database = Operation_Database(self, self.db_path)
        if self.operation_database.is_existing_db_path(self.db_path):
            self.setEnable_file_button(True)
            self.operation_database.connect_database()
            self.table_info = self.operation_database.read_table_info()
        elif self.db_path:
            QMessageBox.information(self, '提示', '没有找到json文件指示的数据库', QMessageBox.Yes)
        else:
            pass

    def init_class(self):
        """ 初始化其他所需启动类 """
        # pass
        self.host_client = Tcp_Connect(self)
        self.second_process = Second_Process(self.queue_second_instruct,
                                             self.queue_second_state,
                                             self.queue_scope_data,
                                             self.queue_logging)
        self.second_process.start()

        # 将scope保存是否启用时间后缀传递给第二进程
        time_foermat = self.scope_time_ext_format.replace('年', '%Y')
        time_foermat = time_foermat.replace('月', '%m')
        time_foermat = time_foermat.replace('日', '%d')
        time_foermat = time_foermat.replace('时', '%H')
        time_foermat = time_foermat.replace('分', '%M')
        time_foermat = time_foermat.replace('秒', '%S')
        self.queue_second_instruct.put([5, self.scope_time_ext_flag, time_foermat])

    def init_timer(self):
        self.refresh_tabel_data_timer = QtCore.QTimer()  # 暂时先不用
        self.save_config_timer = QtCore.QTimer()  # 保存config的定时器
        self.save_config_timer.timeout.connect(self.get_all_config)
        self.save_config_timer.start(5000)
        self.recv_host_data_timer = QtCore.QTimer()  # 刷新host数据的定时器
        self.recv_host_data_timer.timeout.connect(self.recv_host_data_timer_slot)
        self.recv_host_data_timer.start(500)
        self.recv_scope_data_timer = QtCore.QTimer()  # 接收scope数据的定时器
        self.recv_scope_data_timer.timeout.connect(self.recv_scope_data_timer_slot)
        self.recv_scope_data_timer.start(10)

    def connect_siggnal_slot(self):
        """ 关联所有的信号和槽函数"""

        """ 主信号 """
        self.signal_table_info.connect(self.signal_table_info_slot)
        self.signal_operat.connect(self.signal_operat_slot)

        """ 测试用的 """
        self.test_action.triggered.connect(self.test_btn_slot)

        """ 文件相关 """

        self.create_new_config_action.triggered.connect(self.create_new_config_slot)
        self.open_config_action.triggered.connect(self.open_config_action_slot)
        self.open_config_var_action.triggered.connect(self.open_config_var_slot)
        self.reload_config_var_action.triggered.connect(self.reload_config_var_slot)
        self.export_file_action.triggered.connect(self.export_file_slot)
        self.load_default_action.triggered.connect(self.load_default_slot)

        """ 视图相关 """
        self.console_view_action.triggered.connect(self.console_view_slot)
        self.dock_scope_view_action.triggered.connect(self.dock_scope_view_slot)

        """ 管理相关 """
        self.communication_manage_action.triggered.connect(self.communication_manage_slot)
        self.reset_tcp_action.triggered.connect(self.reset_tcp_slot)
        self.program_update_action.triggered.connect(self.program_update_slot)
        self.upload_struct_data_action.triggered.connect(self.upload_struct_data_slot)
        self.com_online_action.triggered.connect(self.com_online_slot)
        self.com_offline_action.triggered.connect(self.com_offline_slot)
        self.save_host_data_action.triggered.connect(self.save_host_slot)
        self.modify_logging_Level_menu.triggered.connect(self.modify_logging_Level_slot)
        self.clear_errLog_action.triggered.connect(self.clear_errLog_slot)
        self.scope_time_ext_menu.triggered.connect(self.scope_time_ext_slot)
        self.scope_time_ext_format_action.triggered.connect(self.scope_time_ext_format_slot)

        """ scope相关 """
        self.scope_start_action.triggered.connect(self.scope_start_slot)
        self.add_scope_view_action.triggered.connect(self.add_scope_view_slot)
        self.channel_manage_action.triggered.connect(self.channel_manage_slot)
        self.scope_data_save_action.triggered.connect(self.scope_data_save_slot)
        self.h5_compression_menu.triggered.connect(self.h5_compression_slot)

        """ EEPROM相关 """
        self.EEPROM_action.triggered.connect(self.EEPROM_slot)

        """ 帮助相关 """
        self.help_action.triggered.connect(self.help_slot)

    """
    槽函数区 ------------------------------------------------------------------------------------------------
    放置所有的槽函数 ------------------------------------------------------------------------------------------
    """
    """ 主槽函数 """

    def signal_table_info_slot(self, action, addrs, values):
        """ table_info 的相关槽函数    修改数值 和 添加控件 (高频信号)"""
        # 由控制台控件触发,修改table和发送给dsp
        if action == 0:
            self.table_update(addrs, values)
            self.tcp_send(addrs, values)
            self.dock_widget_update(addrs, values)
        # 由table触发,修改控制台控件和发送给dsp
        elif action == 1:
            self.table_update(addrs, values)
            self.tcp_send(addrs, values)
            self.dock_widget_update(addrs, values)
        # 由刷新列表触发,修改控制台控件和刷新table
        elif action == 2:
            self.table_update(addrs, values)
            self.dock_widget_update(addrs, values)
        # 给控制台添加按钮
        elif action == 3:
            self.console.add_draggableWidget(widget_type=2, addr=addrs[0])
        # 给控制台添加combox
        elif action == 4:
            self.console.add_draggableWidget(widget_type=0, addr=addrs[0])
        # 给控制台添加edit
        elif action == 5:
            self.console.add_draggableWidget(widget_type=1, addr=addrs[0])
        # 设置缺省值
        elif action == 6:
            self.set_table_info_default(addrs[0], values[0])

    @log_decorator
    def signal_operat_slot(self, action_lv1, action_lv2, values):
        """ 其他类的相关信号 一般为提交事件和关闭事件 (低频信号)"""
        # Dock_Tree的操作  0-双击事件 1-单击事件
        if action_lv1 == 0:
            if action_lv2 == 0:
                self.tab_table.add_widget_to_tab(values[0])
            elif action_lv2 == 1:
                self.tab_table.add_widget_to_dock(values[0])
        # Channel_Manage的操作 0-关闭窗口， 1-保存channel
        elif action_lv1 == 1:
            if action_lv2 == 0:
                self.channel_manage_close()
            elif action_lv2 == 1:
                self.channel_manage_save(values[0])
        # Communication_manage的操作 0-关闭窗口， 1-保存配置
        elif action_lv1 == 2:
            if action_lv2 == 0:
                self.communication_manage_close()
            elif action_lv2 == 1:
                self.communication_manage_save(values[0])
            elif action_lv2 == 2:
                self.communication_manage_write_e2promIP(values[0], values[1])
            elif action_lv2 == 3:
                self.communication_manage_restart_CPU()
            elif action_lv2 == 4:
                self.communication_manage_get_e2prom_IP()
        # E2prom_Manage的操作  0-关闭窗口, 1-操作dsp的e2prom, 2-保存当前值到缺省值
        elif action_lv1 == 3:
            if action_lv2 == 0:
                self.e2prom_manage_close()
            elif action_lv2 == 1:
                self.e2prom_manage_control(values[0], values[1], values[2])
            elif action_lv2 == 2:
                self.e2prom_manage_save()
        # program_update的操作 0-关闭窗口并保存config, 1-开始更新前的处理, 2-更新完成后的动作, 3-获取版本和时间, 4-确认升级
        elif action_lv1 == 4:
            if action_lv2 == 0:
                self.program_update_close(values[0])
            elif action_lv2 == 1:
                self.program_update_start()
            elif action_lv2 == 2:
                self.program_update_done()
            elif action_lv2 == 3:
                self.program_update_get_e2prom()
            elif action_lv2 == 4:
                self.program_update_agree_update(values[0])
        # tcp_reset_manage的操作   0-关闭窗口并保存config, 1-开始执行重置TCP
        elif action_lv1 == 5:
            if action_lv2 == 0:
                self.tcp_reset_manage_close(values[0])
            elif action_lv2 == 1:
                self.com_offline_slot(QBox=False)
        # 收到视图的操作信号 0-关闭视图响应, 1-打开视图响应
        elif action_lv1 == 6:
            if action_lv2 == 0:
                self.view_action_set(False, values[0])
            elif action_lv2 == 1:
                self.view_action_set(True, values[0])
        # Tcp_Connect的操作信号  0-掉线处理并响应原因, 1-丢失数据的响应并显示原因
        elif action_lv1 == 7:
            if action_lv2 == 0:
                self.tcp_connect_discnnect(values[0])
            elif action_lv2 == 1:
                self.statubar.showMessage(values[0], 1000)
        # Operation_Database的操作
        elif action_lv1 == 8:
            self.config_var_table_close(action_lv2)
        # struct_write的操作
        elif action_lv1 == 9:
            if action_lv2 == 0:
                self.struct_write_manage_close(values[0])
            elif action_lv2 == 1:
                self.struct_write_changeAddr_request(values[0])
            elif action_lv2 == 2:
                self.struct_write_send_data(values[0])
            elif action_lv2 == 3:
                self.struct_write_recover_scope()
            elif action_lv2 == 4:
                self.struct_write_mult_start()
            elif action_lv2 == 5:
                self.struct_write_mult_changeAddr_request(values[0])
            elif action_lv2 == 6:
                self.struct_write_send_mult_data(values[0])


    def test_btn_slot(self):
        # self.get_all_config()
        # self.load_class()
        # print(self.a)
        # if self.a == 0:
        #     self.a = 1
        #     self.scope_size = [self.dock_scope.scroll_area.size().width(), self.dock_scope.scroll_area.size().height()]
        #     self.console_size = [self.console.scroll_area.size().width(), self.console.scroll_area.size().height()]
        # elif self.a == 1:
        #     self.a = 0
        #     self.dock_scope.scroll_area.resize(QSize(self.scope_size[0], self.scope_size[1]))
        #     self.console.scroll_area.resize(QSize(self.console_size[0], self.console_size[1]))

        # self.dock_scope.update_buf(self.scope_time, self.scope_data, self.scope_index)
        # print(self.operation_database.get_hostconfig_hash(str))
        # print(self.operation_database.get_hostconfig_hash(list))
        pass


    """ 定时器相关 """

    def refresh_table_data_timer_slot(self):
        """ 刷新表格的 槽函数 """
        pass

    def recv_host_data_timer_slot(self):
        """ 接收host 数据的槽函数 """
        if self.conn_flag:
            result = self.host_client.read_mult_registers(0, 499)
            if result is None:
                pass
            else:
                self.signal_table_info.emit(2, list(range(len(self.table_info['var_current']))),
                                            list(result[:len(self.table_info['var_current'])]))

            # 保存host数据
            if self.host_save_flag:
                self.host_save_replace_count -= 1
                if self.host_save_replace_count == 0:
                    self.host_save_replace_count = 10
                    with open(self.host_save_name, 'a+', encoding='utf-8') as f:
                        text = ''
                        for i in range(len(self.table_info['var_current'])):
                            if self.table_info['isLogged'][i] == 1:
                                if self.table_info['var_type'][i] in ['5', 'bit', '3', 'enum']:
                                    text += '%d,' % result[i]
                                else:
                                    text += '%.4f,' % result[i]
                            else:
                                pass
                        f.write(text[:-1] + '\n')

            # 当发生E2prom操作时间后等待响应
            if self.e2prom_change_flag:
                if 1 < self.e2prom_response_count < 10:
                    result = self.host_client.control_e2prom(3)
                    if result:
                        if self.e2prom_IP_flag:
                            self.e2prom_IP_flag = False
                            self.communication_manage.e2prom_IP_response('写入成功')
                            self.config_all['communication_info']['IP'] = self.e2prom_IP_str
                            if self.communication_manage:
                                self.communication_manage.update_IP(self.e2prom_IP_str)
                            self.config_all['program_update']['IP'] = self.e2prom_IP_str
                            if self.program_update:
                                self.program_update.update_IP(self.e2prom_IP_str)
                        else:
                            self.e2prom_manage.response('操作成功')
                        self.e2prom_change_flag = False
                        self.e2prom_response_count = 0
                    else:
                        self.e2prom_response_count += 1
                elif self.e2prom_response_count > 10:
                    self.host_client.control_e2prom(4)
                    if self.e2prom_IP_flag:
                        self.e2prom_IP_flag = False
                        self.communication_manage.e2prom_IP_response('写入失败')
                    else:
                        self.e2prom_manage.response('操作失败')
                    self.e2prom_change_flag = False
                    self.e2prom_response_count = 0
                else:
                    self.e2prom_response_count += 1


    def recv_scope_data_timer_slot(self):
        """ 接收 scope数据的槽函数 """
        # 从第二进程接收反馈
        if self.queue_second_state.qsize():
            state = self.queue_second_state.get()
            # 连接成功
            if state == [1, 1, 1]:
                if self.conn_flag:
                    self.scope_start_action.setEnabled(True)
                else:
                    self.scope_start_action.setEnabled(False)
            # 连接失败
            elif state == [1, 1, 0]:
                self.scope_start_action.setEnabled(False)
            # 断开成功
            elif state == [1, 0, 1]:
                self.scope_start_action.setEnabled(False)
                if self.scope_start_action.isChecked():
                    self.scope_start_action.setChecked(False)
            # 第二进程通信掉线重新处理
            elif state == [3, 2]:
                if self.conn_flag:
                    # if self.host_client.read_scope_stop():
                    if self.host_client.read_scope_continue():
                        pass
                    else:
                        # my_logger.warning('重新连接并继续发送scope失败-continue')
                        self.queue_second_state.put([6, 2, '重新连接并继续发送scope失败-continue'])
                    # else:
                    #     # my_logger.warning('重新连接并继续发送scope失败-stop')
                    #     self.queue_second_state.put([6, 2, '重新连接并继续发送scope失败-stop'])
            # 第二进程保存文件失败的响应
            elif state == [4, 0]:
                if self.scope_data_save_action.isChecked():
                    self.scope_data_save_action.setChecked(False)
                    self.scope_data_save_action.setText('保存')
                    QMessageBox.information(self, '提示', 'h5py文件生成出错，可能原因为压缩格式有问题，请重试',
                                            QMessageBox.Yes)
            # 第二进程出现重大错误
            elif state == [5, 0]:
                QMessageBox.information(self, '提示', '第二进程出现重大错误，请查看日志',
                                        QMessageBox.Yes)

            # 第二进程的日志输出
            elif state[0] == 6:
                print('有遗漏')
                # if state[1] == 0:
                #     self.my_logger.debug(state[2])
                # elif state[1] == 1:
                #     self.my_logger.info(state[2])
                # elif state[1] == 2:
                #     self.my_logger.warning(state[2])
                # elif state[1] == 3:
                #     self.my_logger.error(state[2])

        # 接收日志的消息队列
        if self.queue_logging.qsize():
            state = self.queue_logging.get()
            if state[0] == 1:
                if state[1] == 0:
                    self.my_logger.debug(state[2])
                elif state[1] == 1:
                    self.my_logger.info(state[2])
                elif state[1] == 2:
                    self.my_logger.warning(state[2])
                elif state[1] == 3:
                    self.my_logger.error(state[2])


    """ 文件相关 """

    @log_decorator
    def create_new_config_slot(self):
        """ 新建配置文件 """
        if self.conn_flag:
            QMessageBox.warning(self, "警告", "请先退出连接再操作", QMessageBox.Yes)
        else:
            with open('.path', 'r', encoding='utf-8') as f:
                db_path = json.load(f)[0]
            file_path, filetype = QFileDialog.getSaveFileName(self,
                                                              "选择数据库文件",
                                                              db_path,
                                                              "Database Files (*.db)")
            if file_path:
                # 切换之前先保存一下当前状态
                self.get_all_config()
                # 切换数据库
                self.operation_database.reconnect_new_database(file_path)
                self.change_database_flag = True
                self.db_path = file_path
                # 重新读取新的config_all
                self.read_config()
                # 保存新数据库路径
                with open('.path', 'w', encoding='utf-8') as f:
                    json.dump([file_path], f, ensure_ascii=False, indent=4, separators=(',', ':'))
                self.setEnable_all_button(False)
                self.reload_config_var_slot()
            else:
                pass

    @log_decorator
    def open_config_action_slot(self):
        """ 打开配置文件 """
        if self.conn_flag:
            QMessageBox.warning(self, "警告", "请先退出连接再操作", QMessageBox.Yes)
        else:
            with open('.path', 'r', encoding='utf-8') as f:
                db_path = json.load(f)[0]
            file_path, filetype = QFileDialog.getOpenFileName(self,
                                                              "选择数据库文件",
                                                              db_path,
                                                              "Database Files (*.db)")
            if file_path:
                # 切换之前先保存一下当前状态
                self.get_all_config()
                # 切换数据库
                self.operation_database.reconnect_database(file_path)
                self.db_path = file_path
                self.change_database_flag = True
                # 重新读取新的config_all
                self.read_config()
                # 保存新数据库路径
                with open('.path', 'w', encoding='utf-8') as f:
                    json.dump([file_path], f, ensure_ascii=False, indent=4, separators=(',', ':'))
                self.setEnable_all_button(False)
                self.reload_config_var_slot()
            else:
                pass

    @log_decorator
    def open_config_var_slot(self):
        """ 打开配置表进行修改 """
        if self.config_var_table:
            pass
        else:
            self.config_var_table = Config(self, configFile=self.db_path)
        self.config_var_table.editVarTable(self.com_online_action.isChecked())

    @log_decorator
    def reload_config_var_slot(self):
        """ 重新导入 table_info """
        if self.conn_flag:
            QMessageBox.information(self, '提示', '请先断开连接!', QMessageBox.Yes)
            return
        if self.change_database_flag:
            self.change_database_flag = False
        else:
            self.get_all_config()
        self.table_info = self.operation_database.read_table_info()
        self.load_class()
        self.setEnable_file_button(True)

    @log_decorator
    def export_file_slot(self):
        """ 导出从配置文件里加载出来的头文件 """
        if self.conn_flag:
            QMessageBox.information(self, '提示', '请先断开连接,再尝试操作!', QMessageBox.Yes)
        else:
            text, ok = QInputDialog.getText(self, '自定义', '前缀:地址(注意格式!)',
                                            text=self.header_prefix + ':' + self.header_name)
            if ok:
                try:
                    if ':' in text:
                        self.header_prefix = text.split(':')[0]
                        self.header_name = text.split(':')[1]
                        load_way = self.header_path
                        if not load_way:
                            load_way = '.'
                        if not os.path.exists(load_way):
                            load_way = '.'
                        file_path, file_type = QFileDialog.getSaveFileName(self, '保存头文件', load_way,
                                                                           '头文件(*.h);;所有文件(*)')
                        if file_path:
                            file_name = file_path.split('/')[-1].split('.')[0]
                            header_data = self.operation_database.export_header(self.header_prefix, self.header_name,
                                                                                file_name)
                            with open(file_path, 'w', encoding='utf-8') as f:
                                f.write(header_data)
                            self.header_path = file_path
                            reply = QMessageBox.information(self, "提示", "文件保存成功是否查看",
                                                            QMessageBox.Yes | QMessageBox.No)
                            if reply == QMessageBox.Yes:
                                os.startfile(File_Process.get_record_path(self.header_path))
                            else:
                                pass
                        else:
                            pass
                    else:
                        QMessageBox.information(self, "提示", "请注意输入格式!", QMessageBox.Yes)
                except Exception as e:
                    exc_info = traceback.format_exc()
                    # my_logger.warning('头文件导出失败， ' + exc_info)
                    self.queue_second_state.put([6, 2, '头文件导出失败， ' + exc_info])
                    QMessageBox.information(self, "提示", "写入失败，请重试", QMessageBox.Yes)
            else:
                pass

    @log_decorator
    def load_default_slot(self, MSG=True):
        """ 加载缺省值 """
        if not self.conn_flag:
            QMessageBox.information(self, '提示', '请先连接,再尝试操作!', QMessageBox.Yes)
            return
        if MSG:
            reply = QMessageBox.question(self, "提示", "是否确认加载缺省值", QMessageBox.Yes | QMessageBox.No)
            if reply == QMessageBox.Yes:
                var_addr_list = []
                var_value_list = []
                for i in range(len(self.table_info['var_current'])):
                    if self.table_info['isEEPROM'][i] == 0 and self.table_info['isWritable'][i] == 1:
                        var_addr_list.append(self.table_info['addr'][i])
                        if self.table_info['var_default'][i]:
                            if self.table_info['var_type'][i] in ['5', 'bit', '3', 'enum', '1', 'int']:
                                var_value_list.append(int(self.table_info['var_default'][i]))
                            else:
                                var_value_list.append(float(self.table_info['var_default'][i]))
                        else:
                            if self.table_info['var_type'][i] in ['5', 'bit', '3', 'enum', '1', 'int']:
                                var_value_list.append(int(0))
                            else:
                                var_value_list.append(float(0.0))

                for i in range(len(var_addr_list) - 1):
                    if var_addr_list[i] != var_addr_list[i + 1] - 1:
                        # my_logger.warning('加载缺省值时出现不连续的地址:' + str(var_addr_list))
                        self.queue_logging.put([1, 2, '加载缺省值时出现不连续的地址:' + str(var_addr_list)])
                        QMessageBox.information(self, '提示', '加载缺省值出现错误\n请查看日志', QMessageBox.Yes)
                        return
                self.signal_table_info.emit(1, var_addr_list, var_value_list)
                self.host_client.get_defaultFlag(1)
                QMessageBox.information(self, '提示', '加载成功', QMessageBox.Yes)
            else:
                pass
        else:
            var_addr_list = []
            var_value_list = []
            for i in range(len(self.table_info['var_current'])):
                if self.table_info['isEEPROM'][i] == 0 and self.table_info['isWritable'][i] == 1:
                    var_addr_list.append(self.table_info['addr'][i])
                    if self.table_info['var_default'][i]:
                        if self.table_info['var_type'][i] in ['5', 'bit', '3', 'enum', '1', 'int']:
                            var_value_list.append(int(self.table_info['var_default'][i]))
                        else:
                            var_value_list.append(float(self.table_info['var_default'][i]))
                    else:
                        if self.table_info['var_type'][i] in ['5', 'bit', '3', 'enum', '1', 'int']:
                            var_value_list.append(int(0))
                        else:
                            var_value_list.append(float(0.0))

            for i in range(len(var_addr_list) - 1):
                if var_addr_list[i] != var_addr_list[i + 1] - 1:
                    # my_logger.warning('加载缺省值时出现不连续的地址:' + str(var_addr_list))
                    self.queue_second_state.put([1, 2, '加载缺省值时出现不连续的地址:' + str(var_addr_list)])
                    QMessageBox.information(self, '提示', '加载缺省值出现错误\n请查看日志', QMessageBox.Yes)
                    return
            self.signal_table_info.emit(1, var_addr_list, var_value_list)
            self.host_client.get_defaultFlag(1)

    """ 视图相关 """

    @log_decorator
    def console_view_slot(self):
        """ 打开关闭控制台视图 """
        if self.console_view_action.isChecked():
            self.console.show()
        else:
            self.console.close()

    @log_decorator
    def dock_scope_view_slot(self):
        """ 打开关闭 scope 视图 """
        if self.dock_scope_view_action.isChecked():
            self.dock_scope.show()
        else:
            self.dock_scope.close()

    """ 管理相关 """

    @log_decorator
    def communication_manage_slot(self):
        """ 打开通信配置参数页面 """
        if self.communication_manage:
            if self.communication_manage.isVisible():
                self.communication_manage.setHidden(True)
                self.communication_manage.setHidden(False)
        else:
            self.communication_manage = Communication_Manage(self, self.com_online_action,
                                                             self.config_all['communication_info'])

    @log_decorator
    def reset_tcp_slot(self):
        """ 打开通过sci重置 tcp 的页面 """
        if self.tcp_reset:
            if self.tcp_reset.isVisible():
                self.tcp_reset.setHidden(True)
                self.tcp_reset.setHidden(False)
        else:
            self.tcp_reset = Tcp_Reset_Manage(self, self.config_all['tcp_reset'])

    @log_decorator
    def program_update_slot(self):
        """ 打开远程升级的页面 """
        if self.program_update:
            if self.program_update.isVisible():
                self.program_update.setHidden(True)
                self.program_update.setHidden(False)
        else:
            self.program_update = Program_Update(self, self.config_all['program_update'])
            self.program_update.get_e2prom_ver_time()

    @log_decorator
    def upload_struct_data_slot(self):
        if self.struct_write_manage:
            if self.struct_write_manage.isVisible():
                self.struct_write_manage.setHidden(True)
                self.struct_write_manage.setHidden(False)
        else:
            size_list = []
            if self.conn_flag:
                result = self.host_client.get_writeable_memory()
                if type(result) == list:
                    size_list = result
            self.struct_write_manage = Struct_Write_Manage(self, self.config_all['struct_write'], size_list, self.com_online_action)

    @log_decorator
    def com_online_slot(self):
        """ 上线 """
        if self.config_var_table:
            self.com_online_action.setChecked(False)
            QMessageBox.information(self, "提示", "请先关闭变量修改", QMessageBox.Yes)
            return
        if self.com_online_action.isChecked():
            self.com_online_action.setEnabled(False)
            str_type = ''
            for idx in self.table_info['var_type']:
                if idx in ['5', '3', '1', 'bit', 'enum', 'int']:
                    str_type += 'L'
                else:
                    str_type += 'f'
            if len(str_type) < 500:
                for i in range(500 - len(str_type)):
                    str_type += 'f'
            str_type += 'LLL'  # host的501用于存储IP地址 502，503 progeram的ver 和 time
            hash_list = self.operation_database.get_hostconfig_hash(list)
            result = self.host_client.connect(str_type, hash_list)
            if result == 'succeed':
                # self.conn_done()
                if self.QMSG_dialog_flag:
                    QMessageBox.information(self, "提示", "连接成功", QMessageBox.Yes)
                else:
                    pass
                self.conn_done()
            elif result == 'auth_failed':
                self.com_offline_slot(QBox=False)
                QMessageBox.information(self, "提示", "认证失败", QMessageBox.Yes)
            elif result == 'hash_failed':
                self.disconn_done()
                self.host_client.create_client()
                QMessageBox.information(self, "提示", "上位机与下位机变量参数不一致", QMessageBox.Yes)
            elif result == 'timeout':
                self.disconn_done()
                QMessageBox.information(self, "提示", "连接超时", QMessageBox.Yes)
            elif result == '远端服务异常':
                self.disconn_done()
                QMessageBox.information(self, "提示", "远端服务异常", QMessageBox.Yes)
            elif result == 'bootloader':
                self.disconn_done()
                QMessageBox.information(self, "提示", "当前系统没有PROGRAM程序", QMessageBox.Yes)
        else:
            self.com_offline_slot()

    @log_decorator
    def com_offline_slot(self, state=0, QBox=True):
        """ 下线 """
        if self.conn_flag:
            # 如果正在接收scope数据，先停止
            if self.scope_start_action.isChecked():
                self.scope_recv_flag = True
                self.scope_start_action.setChecked(False)
                self.scope_start_slot(reconn_flag=False)
            else:
                self.scope_recv_flag = False
            if self.host_client.disconnect():
                self.disconn_done()
                if QBox:
                    QMessageBox.information(self, "提示", "离线成功", QMessageBox.Yes)
                else:
                    pass
            else:
                QMessageBox.information(self, "提示", "离线失败", QMessageBox.Yes)
        else:
            if self.com_online_action.isChecked():
                self.com_online_action.setChecked(False)
            if not self.com_online_action.isEnabled():
                self.com_online_action.setEnabled(True)
            QMessageBox.information(self, "提示", "通信未连接", QMessageBox.Yes)

    @log_decorator
    def save_host_slot(self, state):
        """ 保存host数据 """
        if self.save_host_data_action.isChecked():
            self.host_save_name = time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime()) + '.log'
            name_list = []
            for i in range(len(self.table_info['var_comment'])):
                if self.table_info['isLogged'][i] == 1:
                    name_list.append(self.table_info['var_comment'][i])
            with open(self.host_save_name, 'a+', encoding='utf-8') as f:
                f.write(str(name_list)[1:-1].replace("'", '').replace(' ', '') + '\n')
            self.host_save_flag = True
        else:
            self.host_save_flag = False

    """ scope相关 """

    @log_decorator
    def scope_start_slot(self, state=0, reconn_flag=True):
        """ scope数据的接收和停止 """
        if self.scope_start_action.isChecked():
            self.queue_second_instruct.put([3, 1])
            # 增加线程启动的提前量，以确保线程等待数据，而不是数据等待线程
            time.sleep(0.1)
            self.host_client.read_scope_start()
            self.scope_data_save_action.setEnabled(True)
            self.scope_start_action.setText('结束')
        else:
            self.queue_second_instruct.put([3, 0])
            self.host_client.read_scope_stop()
            self.scope_start_action.setText('开始')
            if self.scope_data_save_action.isChecked():
                self.scope_data_save_action.setChecked(False)
                self.scope_data_save_slot()
                self.scope_data_save_action.setEnabled(False)

            #     self.queue_second_instruct.put([4, 0])
            # self.scope_data_save_action.setText('保存')
            # self.scope_data_save_action.setChecked(False)
            # self.scope_data_save_action.setEnabled(False)
            self.dock_scope.update_channel(self.config_all['channel_manage'], self.config_all['channel_manage']['scope_interval'], True)
            self.host_client.send_scope_channel_count(int(self.config_all['channel_manage']['channel_num']), self.config_all['channel_manage']['scope_interval'])
            if reconn_flag:
                scope_name_list = self.config_all['channel_manage']['channel_name'][
                                  :int(self.config_all['channel_manage']['channel_num'])]
                self.create_share_memory(len(scope_name_list))
                if 'member_name' in self.config_all['channel_manage']:
                    member_name_list = self.config_all['channel_manage']['member_name'][
                                      :int(self.config_all['channel_manage']['channel_num'])]
                else:
                    member_name_list = []
                    for i in range(int(self.config_all['channel_manage']['channel_num'])):
                        member_name_list.append('var' + str(i))
                self.queue_second_instruct.put([1, 1, self.config_all['communication_info'], scope_name_list, self.config_all['channel_manage']['scope_interval'], member_name_list])
            else:
                self.queue_second_instruct.put([1, 0])

    @log_decorator
    def add_scope_view_slot(self):
        """ 添加一个scope视图 """
        if self.dock_scope:
            self.dock_scope.add_draggableWidget()
            self.dock_scope.update_channel(self.config_all['channel_manage'],
                                           self.config_all['channel_manage']['scope_interval'], True)
        else:
            QMessageBox.warning(self, "警告", "请先加载文件", QMessageBox.Yes)

    @log_decorator
    def channel_manage_slot(self):
        """ 打开通道管理页面 """
        if self.channel_manage:
            if self.channel_manage.isVisible():
                self.channel_manage.setHidden(True)
                self.channel_manage.setHidden(False)
        else:
            self.channel_manage = Channel_Manage(self, self.config_all['channel_manage'])

    @log_decorator
    def scope_data_save_slot(self):
        """ 保存scope数据到 h5 文件 """
        if self.scope_data_save_action.isChecked():
            load_way = File_Process.get_record_path(self.h5_path)
            if not os.path.exists(load_way):
                load_way = '.'
            if self.scope_time_ext_flag:
                selectedFilter = '带时间后缀的h5文件(*.h5)'
            else:
                selectedFilter = 'h5文件(*.h5)'
            file_path, file_type = QFileDialog.getSaveFileName(self, '保存scope数据', load_way, 'h5文件(*.h5);;带时间后缀的h5文件(*.h5)', selectedFilter=selectedFilter)
            if file_path == '':
                self.scope_data_save_action.setChecked(False)
                return
            else:
                # 先进行是否需要切换时间状态
                if file_type == '带时间后缀的h5文件(*.h5)' and not self.scope_time_ext_flag:
                    self.scope_time_ext_flag = True
                    self.scope_time_ext_enable.setChecked(True)
                    time_foermat = self.scope_time_ext_format.replace('年', '%Y')
                    time_foermat = time_foermat.replace('月', '%m')
                    time_foermat = time_foermat.replace('日', '%d')
                    time_foermat = time_foermat.replace('时', '%H')
                    time_foermat = time_foermat.replace('分', '%M')
                    time_foermat = time_foermat.replace('秒', '%S')
                    self.queue_second_instruct.put([5, self.scope_time_ext_flag, time_foermat])
                    self.get_all_config()
                elif file_type == 'h5文件(*.h5)' and self.scope_time_ext_flag:
                    self.scope_time_ext_flag = False
                    self.scope_time_ext_disable.setChecked(True)
                    time_foermat = self.scope_time_ext_format.replace('年', '%Y')
                    time_foermat = time_foermat.replace('月', '%m')
                    time_foermat = time_foermat.replace('日', '%d')
                    time_foermat = time_foermat.replace('时', '%H')
                    time_foermat = time_foermat.replace('分', '%M')
                    time_foermat = time_foermat.replace('秒', '%S')
                    self.queue_second_instruct.put([5, self.scope_time_ext_flag, time_foermat])
                    self.get_all_config()
                else:
                    pass


                self.h5_path = file_path
                self.scope_data_save_action.setText('停止')
                self.queue_second_instruct.put([4, 1, self.h5_path, self.h5_compression])
        else:
            self.scope_data_save_action.setText('保存')
            self.queue_second_instruct.put([4, 0])

    @log_decorator
    def h5_compression_slot(self, a):
        """ 切换保存scope数据时 h5的压缩格式 """
        if self.scope_data_save_action.isChecked():
            self.h5_compression = a.text()
            QMessageBox.information(self,
                                    "提示",
                                    "压缩格式将在下一次保存中生效!",
                                    QMessageBox.Yes)
        else:
            self.h5_compression = a.text()
        if self.h5_compression == 'None':
            self.h5_None_action.setChecked(True)
            self.h5_gzip_action.setChecked(False)
        elif self.h5_compression == 'gzip':
            self.h5_None_action.setChecked(False)
            self.h5_gzip_action.setChecked(True)

    def modify_logging_Level_slot(self, a):
        self.logging_level = self.logging_level_dict[a.text()]
        self.my_logger.setLevel(self.logging_level)
        self.get_all_config()

    def scope_time_ext_slot(self, a):
        # print(a.text())
        if a.text() == '禁用':
            self.scope_time_ext_flag = False
        else:
            self.scope_time_ext_flag = True

        time_foermat = self.scope_time_ext_format.replace('年', '%Y')
        time_foermat = time_foermat.replace('月', '%m')
        time_foermat = time_foermat.replace('日', '%d')
        time_foermat = time_foermat.replace('时', '%H')
        time_foermat = time_foermat.replace('分', '%M')
        time_foermat = time_foermat.replace('秒', '%S')
        self.queue_second_instruct.put([5, self.scope_time_ext_flag, time_foermat])
        self.get_all_config()

    def scope_time_ext_format_slot(self):
        value_str, ok = QInputDialog.getText(self, "时间戳格式", "请输入时间戳格式，包括允许的特殊字符", QLineEdit.Normal, self.scope_time_ext_format)
        if ok:
            contains = ['年', '月', '日', '时', '分', '秒', '-', '_', ' ', '.', '：']
            for i in range(len(value_str)):
                if value_str[i] not in contains:
                    QMessageBox.information(self, '提示', '输入的字符无效请尝试重新输入\n包括的字符有:' + str(contains), QMessageBox.Yes)
                    return
            self.scope_time_ext_format = value_str

            time_foermat = self.scope_time_ext_format.replace('年', '%Y')
            time_foermat = time_foermat.replace('月', '%m')
            time_foermat = time_foermat.replace('日', '%d')
            time_foermat = time_foermat.replace('时', '%H')
            time_foermat = time_foermat.replace('分', '%M')
            time_foermat = time_foermat.replace('秒', '%S')
            self.queue_second_instruct.put([5, self.scope_time_ext_flag, time_foermat])
            self.get_all_config()
            if self.scope_data_save_action.isChecked():
                QMessageBox.information(self, '提示', '保存效果将在下一次保存中生效!', QMessageBox.Yes)
        else:
            pass




    """ EEPROM相关 """

    @log_decorator
    def EEPROM_slot(self):
        """ 打开EEPROM的窗口 """
        if not self.table_info:
            QMessageBox.information(self, "提示", "没有可用数据", QMessageBox.Yes)
            return
        if self.e2prom_manage:
            if self.e2prom_manage.isVisible():
                self.e2prom_manage.setHidden(True)
                self.e2prom_manage.setHidden(False)
        else:
            self.e2prom_manage = E2prom_Manage(self)

    """ 帮助相关 """

    def help_slot(self):
        """ 打开帮助页面 """
        pass

    """ 
    普通函数区 ----------------------------------------------------------------------------------------------
    
    """
    """ 由信号引发的刷新数据事件 """

    def table_update(self, addrs, values):
        """ 修改表格数据 """
        for i in range(len(addrs)):
            if i < len(self.table_info['var_current']):
                self.table_info['var_current'][addrs[i]] = values[i]
            else:
                break
        self.tab_table.refresh()
        if self.e2prom_manage:
            if self.e2prom_manage.isVisible():
                self.e2prom_manage.refresh(addrs, values)
        # self.dialog_edit.refresh()
        # self.dialog_table.refresh()

    def tcp_send(self, addrs, values):
        """ 发送数据给dsp """
        if self.conn_flag:
            result = self.host_client.write_register(addrs[0], values)
            # print(result)
            if result:
                if len(values) == 1:
                    self.statubar.showMessage('{}写入成功'.format(values[0]), 1000)
                else:
                    self.statubar.showMessage('大批数据写入成功', 1000)
            elif result is None:
                pass

    def dock_widget_update(self, addrs, values):
        """ 修改 console 控件值 """
        self.console.refresh(addrs, values)

    def set_table_info_default(self, addr, value):
        self.table_info['var_default'][addr] = value
        result = self.operation_database.update_default_value_one(self.table_info['uniqueID'][addr], value)
        if not result:
            QMessageBox.information(self, '提示', '默认值修改失败!\n请重试', QMessageBox.Yes)
        else:
            if self.e2prom_manage:
                self.e2prom_manage.update_default_one(addr)

    """ 其他信号的处理函数 """

    @log_decorator
    def channel_manage_close(self):
        """ 关闭 channel_manage 窗口并清理数据 """
        if self.channel_manage:
            self.channel_manage.deleteLater()
            del self.channel_manage
            self.channel_manage = None

    @log_decorator
    def channel_manage_save(self, config):
        """ 保存 channel_manage 提交上来的config并进行更新处理 """
        if self.config_all:
            self.config_all['channel_manage'] = config
            if self.dock_scope:
                if self.scope_start_action.isChecked():
                    self.dock_scope.update_channel(config, self.config_all['channel_manage']['scope_interval'], False)
                else:
                    if self.conn_flag:
                        self.scope_start_slot()

    @log_decorator
    def communication_manage_close(self):
        """ 关闭 communication_manage 窗口并清理数据 """
        if self.communication_manage:
            self.communication_manage.deleteLater()
            del self.communication_manage
            self.communication_manage = None

    @log_decorator
    def communication_manage_save(self, config):
        """ 保存 communication_manage 提交上来的config并进行更新处理 """
        if self.config_all:
            self.config_all['communication_info'] = config
            if self.host_client:
                self.host_client.update_config(config)

    @log_decorator
    def communication_manage_write_e2promIP(self, str_IP, list_IP):
        """ 向下位机的e2prom写入IP地址 存在 500*4 的地址当中 """
        self.e2prom_IP_str = str_IP
        e2prom_IP_num = struct.unpack('>1L', struct.pack('<4B', *tuple(list_IP)))
        if self.host_client.write_register(500, e2prom_IP_num):
            self.e2prom_IP_flag = True
            self.e2prom_manage_control(2, 500, 1)
        else:
            self.communication_manage.e2prom_IP_response('写入失败')

    @log_decorator
    def communication_manage_restart_CPU(self):
        """ 写入地址成功后重启 下位机 """
        self.QMSG_dialog_flag = False
        if self.scope_start_action.isChecked():
            self.scope_recv_flag = True
            self.scope_start_action.setChecked(False)
            self.scope_start_slot()
            self.queue_second_instruct.put([1, 0])
        if self.host_client.restart_CPU():
            self.communication_manage.e2prom_IP_response('重新启动成功')
        else:
            self.communication_manage.e2prom_IP_response('重新启动失败')

    @log_decorator
    def communication_manage_get_e2prom_IP(self):
        """ 从e2prom中获取上一次的IP地址 """
        if self.conn_flag:
            if self.host_client.control_e2prom(1, 500, 1):
                result = self.host_client.read_mult_registers(500, 500)
                e2prom_IP_num = struct.unpack('<4B', struct.pack('>1L', *result))
                self.communication_manage.get_e2prom_IP_response(e2prom_IP_num)

    @log_decorator
    def e2prom_manage_close(self):
        """ 关闭 e2prom_manage 窗口并清理数据 """
        if self.e2prom_manage:
            self.e2prom_manage.deleteLater()
            del self.e2prom_manage
            self.e2prom_manage = None

    @log_decorator
    def e2prom_manage_control(self, opertion_code, start, lenth):
        """ e2prom_manage 对下位机发送的请求函数 """
        if self.conn_flag:
            if self.host_client.control_e2prom(opertion_code, start, lenth):
                self.e2prom_change_flag = True
            else:
                self.e2prom_manage.response('请求失败')
        else:
            self.e2prom_manage.response('通讯未连接')

    @log_decorator
    def e2prom_manage_save(self):
        """ 将 e2prom_manage 的当前值保存到缺省值 """
        if self.operation_database.update_default_value(self.table_info):
            self.e2prom_manage.response('保存成功')
            self.e2prom_manage.update_default()
        else:
            self.e2prom_manage.response('保存失败')

    @log_decorator
    def program_update_close(self, config):
        """ 关闭 program_update 窗口，并清理数据保存 config """
        if self.program_update:
            self.program_update.deleteLater()
            del self.program_update
            self.program_update = None
        self.config_all['program_update'] = config

    @log_decorator
    def struct_write_manage_close(self, config):
        """ 关闭 struct_write_manage 窗口，并清理数据保存 config """
        if self.struct_write_manage:
            self.struct_write_manage.deleteLater()
            del self.struct_write_manage
            self.struct_write_manage = None
        self.config_all['struct_write'] = config

    @log_decorator
    def struct_write_changeAddr_request(self, index):
        if self.conn_flag:
            if self.host_client.set_batch_startAddr(index):
                # print('写入成功')
                # if self.scope_start_action.isChecked():
                #     self.scope_start_action.setChecked(False)
                #     self.scope_start_slot()
                #     self.struct_write_recover_scope_flag = True
                self.struct_write_manage.response_request_start()
            else:
                self.struct_write_manage.show_MSG('写入地址失败!')
        else:
            self.struct_write_manage.show_MSG('请连接网络后重试!')

    @log_decorator
    def struct_write_send_data(self, data):
        result = self.host_client.write_batch_data(data)
        # print(result)
        if result is None or result is not True:
            self.struct_write_manage.response_request_stop()
            return
        self.struct_write_manage.response_request_continue()

    @log_decorator
    def struct_write_recover_scope(self):
        if self.struct_write_recover_scope_flag:
            self.scope_start_action.setChecked(True)
            self.scope_start_slot()
            self.struct_write_recover_scope_flag = False
        else:
            pass

    @log_decorator
    def struct_write_mult_start(self):
        # if self.scope_start_action.isChecked():
        #     self.scope_start_action.setChecked(False)
        #     self.scope_start_slot()
        #     self.struct_write_recover_scope_flag = True
        pass

    @log_decorator
    def struct_write_mult_changeAddr_request(self, index):
        if self.conn_flag:
            if self.host_client.set_batch_startAddr(index):
                # print('写入成功')
                self.struct_write_manage.response_addr_ok()
            else:
                self.struct_write_manage.show_MSG('写入地址失败!')
        else:
            self.struct_write_manage.show_MSG('请连接网络后重试!')

    @log_decorator
    def struct_write_send_mult_data(self, data):
        result = self.host_client.write_batch_data(data)
        if result is None or result is not True:
            self.struct_write_manage.response_request_stop2()
            return
        self.struct_write_manage.response_request_continue2()

    @log_decorator
    def program_update_start(self):
        """ program_update 开始更新的信号 """
        if self.conn_flag:
            self.QMSG_dialog_flag = False
            if self.scope_start_action.isChecked():
                self.scope_recv_flag = True
                self.scope_start_action.setChecked(False)
                self.scope_start_slot()
                self.queue_second_instruct.put([1, 0])
            result = self.host_client.restart_CPU()
            if result:
                self.program_update.thread_start()
                # self.program_update.thread_start()
                # 存一下当前值
                # for i in range(len(self.table_info['var_current'])):
                #     self.var_current_list.append(self.table_info['var_current'][i])
                # self.program_update.thread_start()
            else:
                pass
        else:
            str_type = ''
            for idx in self.table_info['var_type']:
                if idx in ['5', '3', '1', 'bit', 'enum', 'int']:
                    str_type += 'L'
                else:
                    str_type += 'f'
            if len(str_type) < 500:
                for i in range(500 - len(str_type)):
                    str_type += 'f'
            str_type += 'LLL'  # host的501用于存储IP地址 502，503 progeram的ver 和 time
            hash_list = self.operation_database.get_hostconfig_hash(list)
            result = self.host_client.connect(str_type, hash_list)
            # print(1, result)
            if result == 'succeed':
                result = self.host_client.control_e2prom(1, 501, 2)
                if result:
                    result = self.host_client.read_mult_registers(501, 502)
                    self.program_update.get_e2prom_ver_time_response(result[0], result[1])
                self.program_update.request_update()
            elif result == 'hash_failed':
                result = self.host_client.get_VarandTime()
                if result:
                    self.program_update.get_e2prom_ver_time_response(result[0], result[1])
                self.program_update.request_update()
            elif result == 'bootloader':
                self.program_update.thread_start()
            else:
                self.program_update.show_MSG('异常的网络连接\n请取消更新检查后重试')

    @log_decorator
    def program_update_done(self):
        """ program_update 更新完成的信号 """
        self.com_online_action.setChecked(True)
        self.com_online_slot()
        if self.conn_flag:
            # for i in range(len(self.table_info['var_current'])):
            #     if self.table_info['isWritable'][i] == 1 and self.var_current_list:
            #         self.signal_table_info.emit(1, [self.table_info['addr'][i]], [self.var_current_list[i]])
            #     else:
            #         pass
            self.var_current_list = []

    @log_decorator
    def program_update_get_e2prom(self):
        """ 从e2prom中获取program的版本号和时间 """
        if self.conn_flag:
            if self.host_client.control_e2prom(1, 501, 2):
                result = self.host_client.read_mult_registers(501, 502)
                self.program_update.get_e2prom_ver_time_response(result[0], result[1])

    def program_update_agree_update(self, flag):
        if flag:
            result = self.host_client.restart_CPU()
            time.sleep(0.1)  # 添加延时防止下位机没收到重启指令
            if result:
                self.program_update.thread_start()
            self.host_client.disconnect()
        else:
            self.host_client.disconnect()

    @log_decorator
    def tcp_reset_manage_close(self, config):
        """ 关闭 tcp_reset_manage 窗口，并处理数据保存 config """
        if self.tcp_reset:
            self.tcp_reset.deleteLater()
            del self.tcp_reset
            self.tcp_reset = None
        self.config_all['tcp_reset'] = config

    @log_decorator
    def view_action_set(self, flag, class_type):
        """ 通过信号操作视图按钮，改变按钮状态 """
        if class_type == Dock_Console:
            self.console_view_action.setChecked(flag)
        elif class_type == Dock_Scope:
            self.dock_scope_view_action.setChecked(flag)

    @log_decorator
    def tcp_connect_discnnect(self, MSG):
        """ 连接断开 且尝试重连无法连接时触发 """
        self.disconn_done()
        if self.scope_start_action.isChecked():
            self.scope_start_action.setChecked(False)
            self.queue_second_instruct.put([3, 0])
            self.scope_start_action.setText('开始')
            if self.scope_data_save_action.isChecked():
                self.queue_second_instruct.put([4, 0])
            self.scope_data_save_action.setText('保存')
            self.scope_data_save_action.setChecked(False)
            self.scope_data_save_action.setEnabled(False)
            self.dock_scope.update_channel(self.config_all['channel_manage'], self.config_all['channel_manage']['scope_interval'], True)
            self.queue_second_instruct.put([1, 0])
        if self.QMSG_dialog_flag:
            QMessageBox.information(self, '提示', '连接已断开\n' + MSG, QMessageBox.Yes)

    @log_decorator
    def config_var_table_close(self, action_lv2):
        """ 关闭 config_var_table 的响应，根据修改程度响应不同内容
            0-无改动
            1-只需要重读配置文件
            2-需要重读配置文件并生成新的header文件
            3-表格开始排序，主窗口只能等待不能主动操作其他事情
        """
        if action_lv2 == 0:
            self.setEnabled(True)
            self.config_var_table.deleteLater()
            self.config_var_table = None
        elif action_lv2 == 1:
            self.setEnabled(True)
            self.setEnable_all_button(False)
            self.config_var_table.deleteLater()
            self.config_var_table = None
            QMessageBox.information(self, '提示', 'var表已发生改变并已重新进行加载', QMessageBox.Yes)
            self.reload_config_var_slot()
        elif action_lv2 == 2:
            self.setEnabled(True)
            self.config_var_table.deleteLater()
            self.config_var_table = None
            self.setEnable_all_button(False)
            QMessageBox.warning(self, '警告',
                                'var表已发生改变并已重新加载\n请不要忘记生成header文件\n编译并重新烧写CPU1和CPU2!',
                                QMessageBox.Yes)
            self.reload_config_var_slot()
        elif action_lv2 == 3:
            self.setEnabled(False)

    """ 通信的处理结果 """

    def conn_done(self):
        """ 上线成功后的处理动作 """
        self.conn_flag = True
        self.QMSG_dialog_flag = True
        self.com_online_action.setChecked(True)
        self.com_online_action.setEnabled(True)
        self.signal_online_stateChanged.emit(True)
        self.save_host_data_action.setEnabled(True)
        self.statu_label_lamp.setPixmap(self.pix_green)
        # 连接第二进程通信
        if self.config_all['channel_manage']:
            self.dock_scope.update_channel(self.config_all['channel_manage'], self.config_all['channel_manage']['scope_interval'], True)
            self.host_client.send_scope_channel_count(int(self.config_all['channel_manage']['channel_num']), self.config_all['channel_manage']['scope_interval'])
            scope_name_list = self.config_all['channel_manage']['channel_name'][
                              :int(self.config_all['channel_manage']['channel_num'])]
            if 'member_name' in self.config_all['channel_manage']:
                member_name_list = self.config_all['channel_manage']['member_name'][
                                   :int(self.config_all['channel_manage']['channel_num'])]
            else:
                member_name_list = []
                for i in range(int(self.config_all['channel_manage']['channel_num'])):
                    member_name_list.append('var' + str(i))
            self.create_share_memory(len(scope_name_list))
            self.queue_second_instruct.put([1, 1, self.config_all['communication_info'], scope_name_list, self.config_all['channel_manage']['scope_interval'], member_name_list])
        else:
            QMessageBox.information(self, '提示', '请先设置通道', QMessageBox.Yes)
        # 如果更新之前接收scope，烧写完成后继续接收
        # print(2, self.scope_recv_flag)
        if self.scope_recv_flag:
            self.scope_recv_flag = False
            self.scope_start_action.setChecked(True)
            self.scope_start_slot()

        # 判断是否需要提示加载缺省值
        result = self.host_client.get_defaultFlag(0)
        if not result:
            replay = QMessageBox.information(self, '提示', '是否更新缺省值', QMessageBox.Yes | QMessageBox.No)
            if replay == QMessageBox.Yes:
                self.load_default_slot(MSG=False)

    def disconn_done(self):
        """ 离线后的处理动作 """
        self.conn_flag = False
        self.com_online_action.setChecked(False)
        self.com_online_action.setEnabled(True)
        self.signal_online_stateChanged.emit(False)
        self.save_host_data_action.setChecked(False)
        self.save_host_data_action.setEnabled(False)
        self.host_save_flag = False
        self.statu_label_lamp.setPixmap(self.pix_red)
        self.queue_second_instruct.put([1, 0])

    """ 读取文件后的一系列事件 """

    # def get_table_info(self):
    #     """ 通过 csv_path 获取所需表格数据table_info """
    #     if self.csv_path:
    #         result1, result2 = File_Process.get_table_info(self.csv_path, self.logger)
    #         if result1:
    #             if self.table_info:
    #                 del self.table_info
    #             self.table_info = result2
    #             self.load_class()
    #         else:
    #             self.csv_path = ''
    #             QMessageBox.information(self, '提示', result2, QMessageBox.Yes)

    @log_decorator
    def load_class(self):
        """ 获取到table_info后 实例化相关各类 """
        # 使能全局按钮
        # self.setEnable_all_button(True)
        # 更新tcp_connect的config
        if not self.operation_database.is_existing_db_path(self.db_path):
            return
        self.host_client.update_config(self.config_all['communication_info'])
        # 实例化相关类
        if self.center_widget:
            self.center_widget.deleteLater()
            del self.center_widget
        self.center_widget = Center_Widget(self)
        self.setCentralWidget(self.center_widget)
        if self.dock_tree:
            # self.config_all['dock_tree'] = self.dock_tree.get_config()
            self.dock_tree.deleteLater()
            del self.dock_tree
        self.dock_tree = Dock_Tree(self.center_widget, config=self.config_all['dock_tree'])
        if self.tab_table:
            # self.config_all['tab_table'] = self.tab_table.get_config()
            self.tab_table.deleteLater()
            del self.tab_table
        self.tab_table = Tab_Table(self.center_widget, config=self.config_all['tab_table'])
        if self.console:
            # self.config_all['console'] = self.console.get_config()
            self.console.deleteLater()
            del self.console
        self.console = Dock_Console(self.center_widget,
                                    config=self.config_all['console'],
                                    signal_online_stateChanged=self.signal_online_stateChanged,
                                    queue_logging=self.queue_logging,
                                    grid_size=self.grid_size)
        if self.dock_scope:
            # self.config_all['dock_scope'] = self.dock_scope.get_config()
            self.dock_scope.deleteLater()
            del self.dock_scope
        self.dock_scope = Dock_Scope(self,
                                     config=self.config_all['dock_scope'],
                                     title_bar_widgets=self.scope_actions,
                                     signal_online_stateChanged=self.signal_online_stateChanged,
                                     grid_size=self.grid_size)
        self.dock_scope.update_channel(self.config_all['channel_manage'], self.config_all['channel_manage']['scope_interval'], True)

        # 设置全屏显示
        self.setWindowState(Qt.WindowMaximized)

    def setEnable_file_button(self, flag):
        self.setEnable_all_button(flag)
        self.open_config_var_action.setEnabled(flag)
        self.reload_config_var_action.setEnabled(flag)
        self.export_file_action.setEnabled(flag)

    def create_share_memory(self, length):
        self.scope_time = np.ndarray((Scope_Widget.custom_mem_size, 1), dtype=np.uint32, buffer=self.share_mem_time.buf)
        self.scope_data = np.ndarray((Scope_Widget.custom_mem_size, length), dtype=np.float32, buffer=self.share_mem_data.buf)
        self.scope_index = np.ndarray((1,), dtype=np.uint32, buffer=self.share_mem_index.buf)
        if self.dock_scope:
            self.dock_scope.update_buf(self.scope_time, self.scope_data, self.scope_index)

    def destroy_share_memory(self):
        self.share_mem_time.close()
        self.share_mem_data.close()
        self.share_mem_index.close()
        self.share_mem_time.unlink()
        self.share_mem_data.unlink()
        self.share_mem_index.unlink()

    def setEnable_all_button(self, flag):
        """
        关闭和打开部分功能按钮
        """
        # self.reload_file_action.setEnabled(flag)
        # self.export_file_action.setEnabled(flag)
        self.load_default_action.setEnabled(flag)
        self.view_menu.setEnabled(flag)
        self.console_view_action.setEnabled(flag)
        self.dock_scope_view_action.setEnabled(flag)
        self.manage_menu.setEnabled(flag)
        self.communication_manage_action.setEnabled(flag)
        self.reset_tcp_action.setEnabled(flag)
        self.program_update_action.setEnabled(flag)
        self.com_online_action.setEnabled(flag)
        self.com_offline_action.setEnabled(flag)
        self.save_host_data_action.setEnabled(flag)
        self.scope_menu.setEnabled(flag)
        # self.scope_start_action.setEnabled(flag)
        self.add_scope_view_action.setEnabled(flag)
        self.channel_manage_action.setEnabled(flag)
        # self.scope_data_save_action.setEnabled(flag)
        self.h5_compression_menu.setEnabled(flag)
        self.EEPROM_action.setEnabled(flag)
        self.help_action.setEnabled(flag)
        self.test_action.setEnabled(flag)

    """ config 相关"""

    def get_all_config(self):
        """
        在关闭程序时，从各个部分获取config
        把所有config拼接好保存到config.json当中
        """
        if not self.operation_database.is_existing_db_path(self.db_path):
            return
        config = {}
        config['console'] = self.console.get_config()
        config['tab_table'] = self.tab_table.get_config()
        config['dock_tree'] = self.dock_tree.get_config()
        config['file'] = {'h5_path': self.h5_path,
                          'h5_compression': self.h5_compression,
                          'header_path': self.header_path,
                          'header_prefix': self.header_prefix,
                          'header_name': self.header_name}
        config['dock_scope'] = self.dock_scope.get_config()
        config['channel_manage'] = self.config_all['channel_manage']
        config['communication_info'] = self.host_client.get_config()
        config['program_update'] = self.config_all['program_update']
        config['tcp_reset'] = self.config_all['tcp_reset']
        config['logging_level'] = self.logging_level
        config['scope_time_format'] = [self.scope_time_ext_flag, self.scope_time_ext_format]
        config['struct_write'] = self.config_all['struct_write']
        # with open('config.json', 'w', encoding='utf-8') as f:
        #     json.dump(config, f, ensure_ascii=False, indent=4, separators=(',', ':'))
        self.config_all = config

        # 将config存储到数据库
        self.operation_database.write_config_json_all(config)

    @log_decorator
    def read_config(self):
        """
        读取总的config.json
        将各部分的config分配给各部分
        此函数应该在file文件当中读取
        """
        if not self.operation_database.is_existing_db_path(self.db_path):
            return
        self.config_all = self.operation_database.read_config_json_all()
        # # 通过资源文件读取json，加载默认值
        # file = QtCore.QFile(':/config_default.json')
        # f = file.open(QIODevice.ReadOnly | QIODevice.Text)
        # a = QTextStream(file).readAll()
        # self.config_all = json.loads(a)
        #
        # if os.path.exists('config.json'):
        #     with open('config.json', 'r', encoding='utf-8') as f:
        #         self.config_all = json.load(f)
        #
        if self.config_all['file']:
            if self.config_all['file']['h5_path']:
                self.h5_path = self.config_all['file']['h5_path']
            if self.config_all['file']['h5_compression']:
                self.h5_compression = self.config_all['file']['h5_compression']
                if self.h5_compression == 'None':
                    self.h5_None_action.setChecked(True)
                else:
                    self.h5_gzip_action.setChecked(True)
            if self.config_all['file']['header_path']:
                self.header_path = self.config_all['file']['header_path']
            if self.config_all['file']['header_prefix']:
                self.header_prefix = self.config_all['file']['header_prefix']
            if self.config_all['file']['header_name']:
                self.header_name = self.config_all['file']['header_name']
            if 'scope_interval' not in list(self.config_all['channel_manage'].keys()):
                self.config_all['channel_manage']['scope_interval'] = 1

        # 获取日志等级并设置
        if 'logging_level' in self.config_all:
            self.logging_level = self.config_all['logging_level']
            self.my_logger.setLevel(self.logging_level)
            logging_level_text = {value: key for key, value in self.logging_level_dict.items()}[self.logging_level]
            for action in self.logging_level_action_group.actions():
                if action.text() == logging_level_text:
                    action.setChecked(True)

        # 获取scope保存的时间后缀是否启用
        if 'scope_time_format' in self.config_all:
            self.scope_time_ext_flag = self.config_all['scope_time_format'][0]
            self.scope_time_ext_format = self.config_all['scope_time_format'][1]
        if self.scope_time_ext_flag:
            self.scope_time_ext_enable.setChecked(True)
        else:
            self.scope_time_ext_disable.setChecked(True)

        # 添加struct_write的配置
        if 'struct_write' not in self.config_all:
            self.config_all['struct_write'] = None

    """ 
    重载区 -------------------------------------------------------------------------------------------------
    重载事件的相关函数 ---------------------------------------------------------------------------------------
    """

    def moveEvent(self, event):
        cursor_pos = QCursor.pos()
        screen_list = QGuiApplication.screens()
        # print(screen_list)

        for screen in screen_list:
            if screen.geometry().contains(cursor_pos):
                # print(screen.availableGeometry())
                # print(screen.devicePixelRatio())
                if QGuiApplication.primaryScreen().devicePixelRatio() == 1.0:
                    screen_x = int(screen.size().width() / screen.devicePixelRatio())
                    screen_y = int(screen.size().height() / screen.devicePixelRatio())
                else:
                    screen_x = int(screen.size().width())
                    screen_y = int(screen.size().height())
                # print([screen_x, screen_y])
                if [screen_x, screen_y] != self.screen_ratio and screen_x >= 1097 and screen_y >= 617:
                    # print(screen.size())
                    screen_ratio = [screen_x, screen_y]
                    # print(screen_ratio)
                    self.grid_size = [int(50 / 1920 * screen_ratio[0]), int(50 / 1080 * screen_ratio[1])]
                    self.screen_ratio = screen_ratio
                    # print(self.grid_size)
                    self.load_class()
                    if self.dock_scope:
                        self.dock_scope.update_buf(self.scope_time, self.scope_data, self.scope_index)
        super().moveEvent(event)

    def closeEvent(self, event):
        """ 重载关闭按钮事件，处理相关东西后再退出程序 """
        self.get_all_config()
        self.queue_second_instruct.put([200, 0])
        if self.conn_flag:
            self.com_offline_slot(QBox=False)
        if self.program_update:
            self.program_update.deleteLater()
            del self.program_update
        self.queue_second_instruct.put([200, ])
        self.recv_host_data_timer.stop()
        self.recv_scope_data_timer.stop()
        self.second_process.terminate()
        self.destroy_share_memory()
        logging.shutdown()  # 关闭日志
        event.accept()
        sys.exit()
